import { DataObject } from 'json2md';
import parser from '../../parser/vue-sfc';
import { IVueSfcParseResult, IVueSfcParseResultMdConfig } from '../../types/vue-sfc';
import { formatArrayItemToMdValue, formatToMdValueByType, generateFlow, isNotEmptyObject } from '../../utils';
import { splicParamsContent, splicOptionsContent, splicSlotAttrsContent, splicReturnsContent } from './utils';

export default generateDoc;

/**
 * 输出文档
 */
function generateDoc(options: {
    input: string;
    output: string;
    parseResultPoint?: (parseResult: IVueSfcParseResult) => IVueSfcParseResult;
    formatContentConfigsPoint?: (configs: IVueSfcParseResultMdConfig) => IVueSfcParseResultMdConfig;
    markdownJsonPoint?: (configs: DataObject[]) => DataObject[];
    markdownContentPoint?: (mdContent: string) => string;
    success?: () => void;
}) {
    try {
        const res = parser(options.input);
        if (res) {
            generateFlow(res, {
                output: options.output,

                getFormatContentConfigs(parseResult) {
                    const configs: IVueSfcParseResultMdConfig = { template: {}, script: {} };

                    if (parseResult.desp) configs.desp = parseResult.desp;
                    // @ts-ignore
                    if (parseResult?.script?.name) configs.script.name = parseResult.script.name;

                    const propsConfigs = buildProps(parseResult);
                    const eventsConfigs = buildEvents(parseResult);
                    const methodsConfigs = buildMethods(parseResult);
                    const slotsConfigs = buildSlots(parseResult);
                    // @ts-ignore
                    if (propsConfigs.length) configs.script.props = propsConfigs;
                    // @ts-ignore
                    if (eventsConfigs.length) configs.script.events = eventsConfigs;
                    // @ts-ignore
                    if (methodsConfigs.length) configs.script.methods = methodsConfigs;
                    // @ts-ignore
                    if (slotsConfigs.length) configs.template.slots = slotsConfigs;

                    // @ts-ignore
                    if (!isNotEmptyObject(configs.template)) delete configs.template;
                    // @ts-ignore
                    if (!isNotEmptyObject(configs.script)) delete configs.script;

                    return configs;
                },

                getMarkdownConfigs(configs) {
                    let rets: DataObject[] = [];

                    if (configs?.script?.name) rets.push({ h1: configs.script.name });
                    if (configs.desp) rets.push({ p: configs.desp });

                    rets = rets.concat(generateProps(configs));
                    rets = rets.concat(generateMethods(configs));
                    rets = rets.concat(generateEvents(configs));
                    rets = rets.concat(generateSlots(configs));

                    return rets;
                },

                parseResultPoint: options.parseResultPoint,
                formatContentConfigsPoint: options.formatContentConfigsPoint,
                markdownJsonPoint: options.markdownJsonPoint,
                markdownContentPoint: options.markdownContentPoint,
                success: options.success
            });
        }
    } catch (error) {
        throw error;
    }
}

/**
 * 构建 Props 文档格式
 */
function buildProps(parseResult: IVueSfcParseResult) {
    const propsConfigs = [];
    const { script } = parseResult;

    if (script) {
        const { props, model, events } = script;

        if (props) {
            for (const name in props) {
                let type = '';
                let newName = name;

                const value = props[name];

                // 处理 Prop 多类型情况
                if (value.type) {
                    type = value.type.join(' \\| ');
                }

                // 处理 v-model 情况
                if (name === model.prop.value) newName += ' / v-model';

                // 处理必填情况
                if (value.required) newName += '（必填）';

                // 处理 .sync 情况
                if (events) {
                    for (const eventName in events) {
                        const event = events[eventName];
                        if (event.behavior === 'update' && eventName === name) {
                            newName += '（支持 .sync 修饰符）';
                        }
                    }
                }

                propsConfigs.push(
                    formatArrayItemToMdValue([newName, value.desp, type, splicOptionsContent(value.options || []), formatToMdValueByType({ value: value.default, type: value.defaultValueType })], {
                        length: 5
                    }) as string[]
                );
            }
        }
    }

    return propsConfigs;
}

/**
 * 构造 Props 文档配置
 */
function generateProps(configs: IVueSfcParseResultMdConfig) {
    const rets: Array<DataObject> = [];
    const { script } = configs;
    if (script?.props) {
        rets.push(
            { h2: '属性（Props）' },
            {
                table: {
                    headers: ['参数', '说明', '类型', '可选值', '默认值'],
                    rows: script.props
                }
            }
        );
    }
    return rets;
}

/**
 * 构建 Events 文档格式
 */
function buildEvents(parseResult: IVueSfcParseResult) {
    const eventsConfigs = [];
    const { script } = parseResult;
    if (script) {
        const { events } = script;
        if (events) {
            for (const name in events) {
                const value = events[name];
                if (value.behavior === 'normal') {
                    eventsConfigs.push(formatArrayItemToMdValue([name, value.desp, splicParamsContent(value.params || [])], { length: 3 }) as string[]);
                }
            }
        }
    }
    return eventsConfigs;
}

/**
 * 构造 Events 文档配置
 */
function generateEvents(configs: IVueSfcParseResultMdConfig) {
    const rets: Array<DataObject> = [];
    const { script } = configs;
    if (script?.events) {
        rets.push(
            { h2: '事件（Events）' },
            {
                table: {
                    headers: ['事件名称', '说明', '回调参数'],
                    rows: script.events
                }
            }
        );
    }
    return rets;
}

/**
 * 构建 Methods 文档格式
 */
function buildMethods(parseResult: IVueSfcParseResult) {
    const methodsConfigs = [];
    const { script } = parseResult;
    if (script) {
        const { methods } = script;
        for (const name in methods) {
            const value = methods[name];
            const item = [name, value.desp, splicParamsContent(value.params || [])];
            if (value.returns) item.push(splicReturnsContent(value.returns));
            methodsConfigs.push(formatArrayItemToMdValue(item, { length: 4 }) as string[]);
        }
    }
    return methodsConfigs;
}

/**
 * 构造 Methods 文档配置
 */
function generateMethods(configs: IVueSfcParseResultMdConfig) {
    const rets: Array<DataObject> = [];
    const { script } = configs;
    if (script?.methods) {
        rets.push(
            { h2: '方法（Methods）' },
            {
                table: {
                    headers: ['方法名', '说明', '参数', '返回值'],
                    rows: script.methods
                }
            }
        );
    }
    return rets;
}

/**
 * 构建 Slots 文档格式
 */
function buildSlots(parseResult: IVueSfcParseResult) {
    const slotsConfigs = [];
    const { template } = parseResult;
    if (template) {
        const { slots } = template;
        for (const slotName in slots) {
            const value = slots[slotName];
            const item = formatArrayItemToMdValue([slotName, value.desp, splicSlotAttrsContent(value.attrs || [])], { length: 3 } as string[]);
            if (slotName === 'default') {
                slotsConfigs.unshift(item);
            } else {
                slotsConfigs.push(item);
            }
        }
    }
    return slotsConfigs;
}

/**
 * 构造 Slots 文档配置
 */
function generateSlots(configs: IVueSfcParseResultMdConfig) {
    const rets: Array<DataObject> = [];
    const { template } = configs;
    if (template?.slots) {
        rets.push(
            { h2: '插槽（Slots）' },
            {
                table: {
                    headers: ['名称', '说明', '属性'],
                    rows: template.slots
                }
            }
        );
    }
    return rets;
}
